package com.lianqi.emcpframework.commons.http.message;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @program emcp-framework
 * @ClassName ResponseMessage
 * @description: 返回消息对象封装
 * @author: sky
 * @create: 2018/12/01 19:02
 */
@ApiModel(description = "返回消息结构")
public class ResponseMessage<T> implements Serializable {

    protected  String message;

    protected int status;

    protected  T result;

    private String code;

    private Long timestamp;

    static final int STATUS_OK = 200;

    static final int STATUS_ERROR = 500;

    static final String DATE_FORMIT = "yyyy-MM-dd HH:mm:ss";

    /**
     * 过滤字段 指定需要序列化的字段
     */
    private transient Map<Class<?>,Set<String>> includes;

    /**
     * 过滤字段：指定不需要序列化的字段
     */
    private transient Map<Class<?>,Set<String>> excludes;

    @ApiModelProperty("业务编码")
    public String getCode() {
        return code;
    }
    @ApiModelProperty(value = "状态码，标识返回状态",required = true)
    public int getStatus() {
        return status;
    }
    @ApiModelProperty("返回消息描述")
    public String getMessage() {
        return message;
    }
    @ApiModelProperty(value = "时间戳",required = true,dataType = "Long")
    public Long getTimestamp() {
        return timestamp;
    }
    @ApiModelProperty("返回结果")
    public T getResult() {
        return result;
    }

    /**
     * 获取不需要序列化的字段
     * @return
     */
    public Map<Class<?>, Set<String>> getExcludes() {
        return excludes;
    }

    /**
     * 获取需要序列化的字段
     * @return
     */
    public Map<Class<?>, Set<String>> getIncludes() {
        return includes;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public void setResult(T result) {
        this.result = result;
    }

    public void setStatus(int status) {
        this.status = status;
    }

    public void setTimestamp(Long timestamp) {
        this.timestamp = timestamp;
    }

    public ResponseMessage<T> status(int status){
        this.status = status;
        return this;
    }

    public ResponseMessage<T> result(T result){
        this.result = result;
        return this;
    }

    public ResponseMessage<T> code(String code){
        this.code = code;
        return this;
    }

    private ResponseMessage<T> putTimeStamp(){
        this.timestamp = System.currentTimeMillis();
        return this;
    }

    public ResponseMessage<T> include(String... fields){
        return include(Arrays.asList(fields));
    }

    public ResponseMessage<T> include(Class<?> type,String... fields){
        return include(type,Arrays.asList(fields));
    }

    public ResponseMessage<T> include(Class<?> type, Collection<String> fields){
        if (includes == null){
            includes = new HashMap<>();
        }
        if (fields == null || fields.isEmpty()){
            return this;
        }
        fields.forEach(field ->{
            if (field.contains(".")){
                String[] temp = field.split("[.]]",2);
                try{
                    Field f = type.getDeclaredField(temp[0]);
                    if (f != null){
                        include(f.getType(),temp[1]);
                    }
                }catch (Exception ignore){

                }
            }else {
                getStringListFromMap(includes,type).add(field);
            }
        });
        return this;
    }

    public ResponseMessage<T> exclude(String... fields){
        return  exclude(Arrays.asList(fields));
    }

    public ResponseMessage<T> exclude(Class<?> type,String... fields){
        return exclude(type,Arrays.asList(fields));
    }

    public ResponseMessage<T> include(Collection<String> fields){
        if (fields == null || fields.isEmpty()){
            return this;
        }
        Class type;
        if (getResult() != null){
            type = getResult().getClass();
        }else {
            return this;
        }
        include(type,fields);
        return this;
    }

    public ResponseMessage<T> exclude(Collection<String> fields){
        if (fields == null || fields.isEmpty()){
            return this;
        }
        Class type;
        if (getResult() != null){
            type = getResult().getClass();
        }else {
            return this;
        }
        exclude(type,fields);
        return this;
    }

    public ResponseMessage<T> exclude(Class<?> type,Collection<String> fields){
        if (excludes == null){
            excludes = new HashMap<>();
        }
        if (fields == null || fields.isEmpty()){
            return this;
        }
        fields.forEach(field ->{
            if (field.contains(".")){
                String[] temp = field.split("[.]]",2);
                try{
                    Field f = type.getDeclaredField(temp[0]);
                    if (f != null){
                        exclude(f.getType(),temp[1]);
                    }
                }catch (Exception ignore){

                }
            }else {
                getStringListFromMap(excludes,type).add(field);
            }
        });
        return this;
    }



    protected Set<String> getStringListFromMap(Map<Class<?>, Set<String>> map, Class<?> type) {
        return map.computeIfAbsent(type,k -> new HashSet<>());
    }

    public static <T> ResponseMessage<T> error(String message){
        return error(STATUS_ERROR,message);
    }

    public static <T> ResponseMessage<T> error(int status, String message) {
        ResponseMessage<T> msg = new ResponseMessage<>();
        msg.message = message;
        msg.status(status);
        return msg.putTimeStamp();
    }

    public static <T> ResponseMessage<T> ok(){
        return ok(null);
    }

    private static <T> ResponseMessage<T> ok(T result) {
        return new ResponseMessage<T>()
                .result(result)
                .status(STATUS_OK)
                .putTimeStamp();
    }


    @Override
    public String toString() {
        return JSON.toJSONStringWithDateFormat(this,DATE_FORMIT);
    }
}

